package org.aplikator.client.shared.rpc.marshaller;

import org.aplikator.client.shared.data.PrimaryKey;
import org.aplikator.client.shared.data.Record;
import org.aplikator.client.shared.rpc.marshaller.DataMarshallingUtils.DataType;
import org.jboss.errai.marshalling.client.api.MarshallingSession;
import org.jboss.errai.marshalling.client.api.annotations.ClientMarshaller;
import org.jboss.errai.marshalling.client.api.annotations.ServerMarshaller;
import org.jboss.errai.marshalling.client.api.json.EJObject;
import org.jboss.errai.marshalling.client.api.json.EJValue;
import org.jboss.errai.marshalling.client.marshallers.AbstractNullableMarshaller;

import java.io.Serializable;
import java.util.List;
import java.util.Set;

@ClientMarshaller(Record.class)
@ServerMarshaller(Record.class)
public class RecordMarshaller extends AbstractNullableMarshaller<Record> {

	/*@Override
	public Class<Record> getTypeHandled() {
		return Record.class;
	}*/

	@Override
	public Record[] getEmptyArray() {
		return new Record[0];
	}

	
	@SuppressWarnings("rawtypes")
        @Override
	public Record doNotNullDemarshall(EJValue o, MarshallingSession ctx) {
		if (o.isObject() != null) {
			Record record = null;
			EJObject object = o.isObject();
			if (DemarshallingUtils.containsAndNotNull(object, "primaryKey")) {
				record = new Record(new PrimaryKey(object.get("primaryKey").isString().stringValue()));				
			} else {
				record = new Record();
			}
			
			if (DemarshallingUtils.containsAndNotNull(object, "data")) {
				EJValue data = object.get("data");
				if (data.isObject() != null) {
					EJObject dataObject = data.isObject();
					Set<String> keySet = dataObject.keySet();
					for (String key : keySet) {
						EJValue val = dataObject.get(key);
						DataType dt = DataMarshallingUtils.DataType.lookup(val);
						if (dt != null) {
							Object value  = dt.demarshall(val, ctx);
							record.setValue(key, value);
						}
					}
				}
			}			
			if (DemarshallingUtils.containsAndNotNull(object, "dirty")) {
				List l = DemarshallingUtils.listUnMarshall(object.get("dirty"), ctx, String.class.getName());
				record.resetDirtyFlags();
				for (Object k : l) {
					record.setDirty((String) k);
				}
			}
			if (DemarshallingUtils.containsAndNotNull(object, "primaryKey")) {
				PrimaryKey pk = new PrimaryKey(object.get("primaryKey").isString().stringValue());
				record.setPrimaryKey(pk);
			}
			if (DemarshallingUtils.containsAndNotNull(object, "ownerPrimaryKey")) {
				PrimaryKey pk = new PrimaryKey(object.get("ownerPrimaryKey").isString().stringValue());
				record.setOwnerPrimaryKey(pk);
			}			
			if (DemarshallingUtils.containsAndNotNull(object, "ownerPropertyId")) {
				record.setOwnerPropertyId(object.get("ownerPropertyId").isString().stringValue());
			}
			
			if (DemarshallingUtils.containsAndNotNull(object, "preview")) {
				record.setPreviewProperty(object.get("preview").isString().stringValue());
			}
			return record;
		} else return null;
	}

	@Override
	public String doNotNullMarshall(Record o, MarshallingSession ctx) {
		StringBuilder builder = new StringBuilder();
		builder.append('{');
		if (o.getPrimaryKey()!= null){
            MarshallingUtils.key("primaryKey", builder).append(':');
            builder.append("\""+o.getPrimaryKey().getSerializationString()+"\"").append(',');
        }
		if (o.getOwnerPrimaryKey()!=null) {
		    MarshallingUtils.key("ownerPrimaryKey", builder).append(':');
		    builder.append("\""+o.getOwnerPrimaryKey().getSerializationString()+"\"").append(',');
		}

		if (!o.getDirtyFields().isEmpty()) {
			MarshallingUtils.key("dirty", builder).append(':');
			MarshallingUtils.listMarshall(builder, o.getDirtyFields(), ctx, String.class.getName());
			builder.append(',');
		}
		
		MarshallingUtils.key("data", builder).append(':');
		builder.append('{');
		Set<String> properties = o.getProperties();
		boolean first = true;
		for (String k : properties) {
			if (!first) builder.append(',');
			MarshallingUtils.key(k, builder).append(':');
			Object val = o.getValue(k);
			DataType dt = DataMarshallingUtils.DataType.lookup((Serializable)val);
			if (dt != null) dt.marshall(builder, (Serializable)val, ctx);
			else throw new UnsupportedOperationException("unsuported type "+val.getClass().getName());
			first = false;
		}
		builder.append('}').append(',');

		MarshallingUtils.string("ownerPropertyId", o.getOwnerPropertyId(), builder);
		if (o.getPreviewProperty() != null) {
			builder.append(',');
			MarshallingUtils.string("preview", o.getPreviewProperty(), builder);
		}

		builder.append('}');
		return builder.toString();
	}
}
